package com.taoge.properties;

import com.taoge.exception.RateLimitConfigException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import org.aspectj.weaver.tools.PointcutParser;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Objects;

/**
 * 限流配置类
 * @author Mr.Tao
 */
@Data
@Component
@ConfigurationProperties(prefix = "rate-limit")
public class RateLimitProperties implements InitializingBean {
    /**
     * 是否开启监控
     */
    private Boolean enabled;

    /**
     * 限流类型（1：漏桶限流， 2：普通时间统计限流, 3：滑动时间窗口限流，-1：全部开启）
     */
    private Integer type;

    /**
     * 具体限流策略配置
     */
    private StrategyConfig strategyConfig;

    @Autowired
    private HttpServletRequest request;

    /**
     * 切点表达式前缀
     */
    private static final String PREFIX = "execution(* ";

    /**
     * 切点表达式后缀
     */
    private static final String SUFFIX = "..*.*(..))";

    /**
     * 获取限流路径
     * @param privatePath 各自限流接口包路径
     * @return 限流路径
     */
    public String getLimitPath(String privatePath) {
        String limitPath = privatePath;
        if (!StringUtils.hasText(limitPath)) {
            if (!StringUtils.hasText(strategyConfig.getPublicLimitPackagePath())) {
                throw new RateLimitConfigException("限流接口所在包路径不能为空，请检查[rate-limit.strategy-config]配置！");
            }
            limitPath = strategyConfig.getPublicLimitPackagePath();
        }
        return limitPath;
    }

    /**
     * 根据限流路径获取切点
     * @param privatePath 限流接口包路径
     * @return 切点
     */
    public AspectJExpressionPointcut getPointcutExpression(String privatePath) {
        String limitPath = getLimitPath(privatePath);
        String pointcutExpression = PREFIX + limitPath + SUFFIX;
        try {
            PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
            pointcutParser.parsePointcutExpression(pointcutExpression);
        } catch (Exception e) {
            throw new RateLimitConfigException("限流接口所在包路径" + limitPath + "有误，请检查[rate-limit.strategy-config]配置！");
        }
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        //  我们实现动态配置只需要把这里的execution 字符串替换成自己从配置文件中获取的配置即可。
        pointcut.setExpression(pointcutExpression);
        return pointcut;
    }

    /**
     * 获取真正的限流key
     * @param annotationValue 限流注解value
     * @param annotationKey   限流注解key
     * @param simpleClassName 类名
     * @param methodName    方法名
     * @param privateStrategy 各自限流策略
     * @return  限流key
     */
    public String getKey(String annotationValue, String annotationKey, String simpleClassName, String methodName, Integer privateStrategy) {
        String limitKey = annotationValue;
        if (!StringUtils.hasText(limitKey)) {
            if (StringUtils.hasText(annotationKey)) {
                limitKey = annotationKey;
            } else {
                limitKey = simpleClassName;
            }
        }
        limitKey = limitKey + ":" + methodName;
        return getKeyByStrategy(privateStrategy, limitKey);
    }

    /**
     * 根据策略选择是否组装需要重新key
     * @param privateStrategy 各自限流策略
     * @param key 初步限流key
     * @return 最终限流ky
     */
    private String getKeyByStrategy(Integer privateStrategy, String key) {
        Integer strategy = privateStrategy;
        if (Objects.isNull(strategy)) {
            if (Objects.isNull(strategyConfig.getPublicLimitStrategy())) {
                strategy = 0;
            } else {
                strategy = strategyConfig.getPublicLimitStrategy();
            }
        }

        if (strategy.equals(1)) {
            key = key + ":ip-" + request.getRemoteAddr();
        }
        return key;
    }

    /**
     * Bean加载完成后校验具体限流测试是否为空
     * @throws Exception 可能出现的异常
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        if (enabled && Objects.isNull(strategyConfig)) {
            throw new RateLimitConfigException("限流策略配置[rate-limit.strategy-config]不能为空！");
        }
    }

}
